using System;
using System.Collections.Generic;
using System.Linq;
using Karma.Framework.Core.Metadata.Description;
using Karma.Framework.Core.Modules.Metadata.Converter;
using Karma.Framework.Core.Modules.Reports.Converter;
using Karma.Framework.Core.Modules.Reports.Definition;
using Karma.Framework.Core.Modules.Reports.Definition.Expression;
using Karma.Framework.Core.Modules.Reports.Repository;
using Karma.Framework.Core.Repository.Configuration;
using Karma.Framework.Core.Security;
using Karma.Framework.Core.Serialization;
using log4net;
using Spring.Expressions;

namespace Karma.Framework.Core.Modules.Reports.Service
{
    public class ReportService : IReportService
    {
        private static readonly ILog LOG = LogManager.GetLogger(typeof(ReportService).Name);

        public ReportService(IReportRepository repo)
        {
            Repository = repo;
        }

        private IReportRepository Repository { get; set; }

        #region Implementation of IReportService

        public IList<object> GetEntityProperty(string entityName, string propertyName)
        {
            return Repository.GetEntityProperty(entityName, propertyName);
        }

        [SerializationConverters(new[] { typeof(ReportSimpleConverter) })]
        public IList<Report> GetReports(string entityName)
        {
            return Repository.GetEntityReports(entityName);
        }

        [SerializationConverters(new[] { typeof(EntityDescriptionConverter) })]
        public EntityDescription GetDescription(string entityName)
        {
            var entity = (from e in RepositoryConfiguration.Entities
                          where e.FullName.Equals(entityName)
                          select e).First();
            return entity.GetDescription();
        }

        public ReportResult ExecuteListReport(ReportSpecification specification)
        {
            var report = Repository.Get(specification.Metadata.Id);

            var expression = ExpressionBuilder.Build(specification.Criterions);
            var compiledExpression = expression.Compile(report);
            ProcessParameters(report, compiledExpression);
            specification.Parameters = compiledExpression.Parameters;
            specification.Query = GetReportQueryExpression(report, compiledExpression.Expression);

            if (LOG.IsDebugEnabled)
            {
                LOG.Debug(string.Format("The query:  {0}", specification.Query));
                LOG.Debug("Analyzing the parameters:");
                foreach (var key in specification.Parameters.Keys)
                {
                    LOG.Debug(string.Format("Key [{0}], value [{1}]", key, specification.Parameters[key]));
                }
            }
            return Repository.ExecuteReport(specification);
        }

        public ReportResult ExecuteGraphReport(ReportSpecification specification)
        {
            throw new NotImplementedException();
        }

        private void ProcessParameters(Report report, CompiledExpression expression)
        {
            if (report.ContextParameters != null && report.ContextParameters.Count > 0)
            {
                for (var i = 0; i < report.ContextParameters.Count; i++)
                {
                    var p = report.ContextParameters[i];
                    expression.Parameters.Add(p.Name, ExpressionEvaluator.GetValue(Context.User, p.Expression));
                }
            }
        }

        protected string GetReportQueryExpression(Report report, string expression)
        {
            var whereIndex = report.Query.IndexOf("where");
            var inserPoint = report.Query.IndexOf(" ", whereIndex);
            return report.Query.Insert(inserPoint, " " + expression + " and ");
        }
        
        #endregion
    }
}